# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

########################################################################
# OPENAPI-URI: /api/org/members
########################################################################
# get:
#   responses:
#     '200':
#       content:
#         application/json:
#           schema:
#             $ref: '#/components/schemas/OrgMembers'
#       description: 200 Response
#     default:
#       content:
#         application/json:
#           schema:
#             $ref: '#/components/schemas/Error'
#       description: unexpected error
#   security:
#   - cookieAuth: []
#   summary: Lists the members of an organisation
# post:
#   requestBody:
#     content:
#       application/json:
#         schema:
#           $ref: '#/components/schemas/defaultWidgetArgs'
#     description: Nothing...
#     required: true
#   responses:
#     '200':
#       content:
#         application/json:
#           schema:
#             type: array
#             items:
#               $ref: '#/components/schemas/OrgMembers'
#       description: 200 Response
#     default:
#       content:
#         application/json:
#           schema:
#             $ref: '#/components/schemas/Error'
#       description: unexpected error
#   security:
#   - cookieAuth: []
#   summary: Lists the members of an organisation
# put:
#   requestBody:
#     content:
#       application/json:
#         schema:
#           $ref: '#/components/schemas/UserAccountEdit'
#     required: true
#   responses:
#     '200':
#       content:
#         application/json:
#           schema:
#             $ref: '#/components/schemas/ActionCompleted'
#       description: 200 Response
#     default:
#       content:
#         application/json:
#           schema:
#             $ref: '#/components/schemas/Error'
#       description: unexpected error
#   security:
#   - cookieAuth: []
#   summary: Invite a person to an organisation
# delete:
#   requestBody:
#     content:
#       application/json:
#         schema:
#           $ref: '#/components/schemas/UserAccountEdit'
#     required: true
#   responses:
#     '200':
#       content:
#         application/json:
#           schema:
#             $ref: '#/components/schemas/ActionCompleted'
#       description: 200 Response
#     default:
#       content:
#         application/json:
#           schema:
#             $ref: '#/components/schemas/Error'
#       description: unexpected error
#   security:
#   - cookieAuth: []
#   summary: Remove a person from an organisation
#
########################################################################


"""
This is the Org list renderer for Kibble
"""

import json
import time
import hashlib


def canInvite(session):
    """ Determine if the user can edit sources in this org """
    if session.user["userlevel"] == "admin":
        return True

    dOrg = session.user["defaultOrganisation"] or "apache"
    if session.DB.ES.exists(index=session.DB.dbname, doc_type="organisation", id=dOrg):
        xorg = session.DB.ES.get(
            index=session.DB.dbname, doc_type="organisation", id=dOrg
        )["_source"]
        if session.user["email"] in xorg["admins"]:
            return True


def run(API, environ, indata, session):
    now = time.time()
    # We need to be logged in for this!
    if not session.user:
        raise API.exception(403, "You must be logged in to use this API endpoint!")

    method = environ["REQUEST_METHOD"]

    #################################################
    # Inviting a new member?                        #
    #################################################
    if method == "PUT":
        if canInvite(session):
            newmember = indata.get("email")
            isadmin = indata.get("admin", False)
            orgid = session.user["defaultOrganisation"] or "apache"
            # Make sure the org exists
            if not session.DB.ES.exists(
                index=session.DB.dbname, doc_type="organisation", id=orgid
            ):
                raise API.exception(403, "No such organisation!")

            # make sure the user account exists
            if not session.DB.ES.exists(
                index=session.DB.dbname, doc_type="useraccount", id=newmember
            ):
                raise API.exception(403, "No such user!")

            # Modify user account
            doc = session.DB.ES.get(
                index=session.DB.dbname, doc_type="useraccount", id=newmember
            )
            if orgid not in doc["_source"]["organisations"]:  # No duplicates, please
                doc["_source"]["organisations"].append(orgid)
                session.DB.ES.index(
                    index=session.DB.dbname,
                    doc_type="useraccount",
                    id=newmember,
                    body=doc["_source"],
                )

            # Get org doc from ES
            doc = session.DB.ES.get(
                index=session.DB.dbname, doc_type="organisation", id=orgid
            )
            if isadmin:
                if newmember not in doc["_source"]["admins"]:
                    doc["_source"]["admins"].append(newmember)
                    # Override old doc
                    session.DB.ES.index(
                        index=session.DB.dbname,
                        doc_type="organisation",
                        id=orgid,
                        body=doc["_source"],
                    )
                    time.sleep(1)  # Bleh!!

            # If an admin, and not us, and reinvited, we purge the admin bit
            elif newmember in doc["_source"]["admins"]:
                if newmember == session.user["email"]:
                    raise API.exception(
                        403, "You can't remove yourself from an organisation."
                    )
                doc["_source"]["admins"].remove(newmember)
                # Override old doc
                session.DB.ES.index(
                    index=session.DB.dbname,
                    doc_type="organisation",
                    id=orgid,
                    body=doc["_source"],
                )
                time.sleep(1)  # Bleh!!
            yield json.dumps({"okay": True, "message": "Member invited!!"})

            return
        else:
            raise API.exception(
                403,
                "Only administrators or organisation owners can invite new members.",
            )

    #################################################
    # DELETE: Remove a member                       #
    #################################################
    if method == "DELETE":
        if canInvite(session):
            memberid = indata.get("email")
            isadmin = indata.get("admin", False)
            orgid = session.user["defaultOrganisation"] or "apache"

            # We can't remove ourselves!
            if memberid == session.user["email"]:
                raise API.exception(
                    403, "You can't remove yourself from an organisation."
                )

            # Make sure the org exists
            if not session.DB.ES.exists(
                index=session.DB.dbname, doc_type="organisation", id=orgid
            ):
                raise API.exception(403, "No such organisation!")

            # make sure the user account exists
            if not session.DB.ES.exists(
                index=session.DB.dbname, doc_type="useraccount", id=memberid
            ):
                raise API.exception(403, "No such user!")

            # Modify user account
            doc = session.DB.ES.get(
                index=session.DB.dbname, doc_type="useraccount", id=memberid
            )
            if orgid in doc["_source"]["organisations"]:  # No duplicates, please
                doc["_source"]["organisations"].remove(orgid)
                session.DB.ES.index(
                    index=session.DB.dbname,
                    doc_type="useraccount",
                    id=memberid,
                    body=doc["_source"],
                )

            # Check is user is admin and remove if so
            # Get org doc from ES
            doc = session.DB.ES.get(
                index=session.DB.dbname, doc_type="organisation", id=orgid
            )
            if memberid in doc["_source"]["admins"]:
                doc["_source"]["admins"].remove(memberid)
                # Override old doc
                session.DB.ES.index(
                    index=session.DB.dbname,
                    doc_type="organisation",
                    id=orgid,
                    body=doc["_source"],
                )
                time.sleep(1)  # Bleh!!

            yield json.dumps({"okay": True, "message": "Member removed!"})
            return
        else:
            raise API.exception(
                403,
                "Only administrators or organisation owners can invite new members.",
            )

    #################################################
    # GET/POST: Display members                     #
    #################################################
    if method in ["GET", "POST"]:
        orgid = session.user["defaultOrganisation"] or "apache"
        if not session.DB.ES.exists(
            index=session.DB.dbname, doc_type="organisation", id=orgid
        ):
            raise API.exception(403, "No such organisation!")

        # Only admins should be able to view this!
        if not canInvite(session):
            raise API.exception(403, "Only organisation owners can view this list.")

        # Find everyone affiliated with this org
        query = {"query": {"bool": {"must": [{"term": {"organisations": orgid}}]}}}
        res = session.DB.ES.search(
            index=session.DB.dbname,
            doc_type="useraccount",
            size=5000,  # TO-DO: make this a scroll??
            body=query,
        )
        members = []
        for doc in res["hits"]["hits"]:
            members.append(doc["_id"])

        # Get org doc from ES
        doc = session.DB.ES.get(
            index=session.DB.dbname, doc_type="organisation", id=orgid
        )
        JSON_OUT = {
            "members": members,
            "admins": doc["_source"]["admins"],
            "okay": True,
            "responseTime": time.time() - now,
        }
        yield json.dumps(JSON_OUT)
